﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace AutoDb {
	/// <summary>
	/// 
	/// </summary>
	sealed class CSharpCodeTemplate : ICodeTemplate {
		private string m_target;

		private LogContext m_logContext;

		/// <summary>
		/// 
		/// </summary>
		private static readonly string[] Keywords = new string[] { 
			"abstract",
            "base",
            "bool",
            "break",
            "byte",
            "char",
            "case",
            "catch",
            "checked",
            "class",
            "const",
            "continue",
            "decimal",
            "default",
            "do",
            "double",
            "else",
            "enum",
            "event",
            "explicit",
            "extern",
            "false",
            "finally",
            "fixed",
            "float",
            "for",
            "foreach",
            "goto",
            "if",
            "implicit",
            "in",
            "interface",
            "internal",
            "lock",
            "long",
            "new",
            "namespace",
            "null",
            "object",
            "operator",
            "out",
            "override",
            "params",
            "public",
            "protected",
            "private",
            "readonly",
            "ref",
            "return",
            "where",
            "throw",
            "true",
            "try",
            "typeof",
            "this",
            "short",
            "struct",
            "sealed",
            "sbyte",
            "static",
            "sizeof",
            "switch",
            "string",
            "void",
            "virtual",
            "volatile",
            "uint",
            "ulong",
            "ushort",
            "unchecked",
            "unsafe",
            "using",
            "while"
		};

		/// <summary>
		/// 
		/// </summary>
		/// <param name="module"></param>
		public void Render(DbModule module) {
			var codeFileName = ToPascal(module.Name) + "Provider.cs";
			var namespaceName = ToPascal(module.Package);

			m_logContext.Verbose(Properties.Main.WritingCodeFile, codeFileName);

			using (var sw = new CodeTextWriter(codeFileName)) {
				WriteHead(sw);
				WriteStartNamespace(sw, namespaceName);

				// Write table(s).
				foreach (var table in module.Tables) {
					WriteTable(sw, table);

					sw.WriteLine();

					WriteTableRow(sw, table);

					sw.WriteLine();
				}

				WriteModule(sw, module);

				WriteEndNamespace(sw, namespaceName);
			}
		}

		#region Utility

		/// <summary>
		/// 
		/// </summary>
		/// <param name="s"></param>
		/// <returns></returns>
		private string ToPascal(string s) {
			return NameHelper.Convert(s, NameHelper.ToPascal);
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="s"></param>
		/// <returns></returns>
		private string ToCamel(string s) {
			return NameHelper.Convert(s, NameHelper.ToCamel);
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="s"></param>
		/// <returns></returns>
		private string EscapeKeyword(string s) {
			if (Keywords.Contains(s)) {
				return "@" + s;
			} else {
				return s;
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="parameter"></param>
		/// <returns></returns>
		private string GetParameterType(DbParameter parameter) {
			System.Diagnostics.Debug.Assert(parameter != null);

			switch (parameter.DbType) {
				case DbType.String:
					return "string";
				case DbType.Integer:
					return "int?";
				case DbType.Long:
					return "long?";
				case DbType.Decimal:
					return "decimal?";
				case DbType.Double:
					return "double?";
				case DbType.Date:
				case DbType.DateTime:
				case DbType.StartDate:
				case DbType.StartMonth:
				case DbType.StartYear:
				case DbType.EndDate:
				case DbType.EndMonth:
				case DbType.EndYear:
					return "global::System.DateTime?";
				case DbType.Boolean:
					return "bool?";
				case DbType.Binary:
					return "byte[]";
				default:
					System.Diagnostics.Debug.Assert(false, "", "Unknown db type: \"{0}\".", parameter.DbType);
					return "void";
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="parameter"></param>
		/// <returns></returns>
		private string GetParameterDefaultValue(DbParameter parameter) {
			System.Diagnostics.Debug.Assert(parameter != null);

			if (parameter.DbType == DbType.String) {
				return "\"\"";
			} else {
				return "null";
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="parameter"></param>
		/// <returns></returns>
		private string GetParameterDbType(DbParameter parameter) {
			System.Diagnostics.Debug.Assert(parameter != null);

			switch (parameter.DbType) {
				case DbType.String:
					return "MyLib.DbParameterType.String";
				case DbType.Integer:
					return "MyLib.DbParameterType.Integer";
				case DbType.Long:
					return "MyLib.DbParameterType.Long";
				case DbType.Decimal:
					return "MyLib.DbParameterType.Decimal";
				case DbType.Double:
					return "MyLib.DbParameterType.Double";
				case DbType.Date:
					return "MyLib.DbParameterType.Date";
				case DbType.DateTime:
					return "MyLib.DbParameterType.DateTime";
				case DbType.StartDate:
					return "MyLib.DbParameterType.StartDate";
				case DbType.StartMonth:
					return "MyLib.DbParameterType.StartMonth";
				case DbType.StartYear:
					return "MyLib.DbParameterType.StartYear";
				case DbType.EndDate:
					return "MyLib.DbParameterType.EndDate";
				case DbType.EndMonth:
					return "MyLib.DbParameterType.EndMonth";
				case DbType.EndYear:
					return "MyLib.DbParameterType.EndYear";
				case DbType.Boolean:
					return "MyLib.DbParameterType.Bool";
				case DbType.Binary:
					return "MyLib.DbParameterType.Binary";
				default:
					System.Diagnostics.Debug.Assert(false, "", "Unknown db type: \"{0}\".", parameter.DbType);
					return "MyLib.DbParameterType.String";
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="parameter"></param>
		/// <returns></returns>
		private string GetParameterMatchType(DbParameter parameter) {
			System.Diagnostics.Debug.Assert(parameter != null);

			switch (parameter.MatchType) {
				case MatchType.Exact:
					return "MyLib.DbParameterMatchType.Exact";
				case MatchType.StartsWith:
					return "MyLib.DbParameterMatchType.StartsWith";
				case MatchType.EndsWith:
					return "MyLib.DbParameterMatchType.EndsWith";
				case MatchType.Contains:
					return "MyLib.DbParameterMatchType.Contains";
				default:
					System.Diagnostics.Debug.Assert(false, "", "Unknown match type: \"{0}\".", parameter.MatchType);
					return "MyLib.DbParameterMatchType.Exact";
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="parameter"></param>
		/// <returns></returns>
		private string GetParameterDirection(DbParameter parameter) {
			System.Diagnostics.Debug.Assert(parameter != null);

			switch (parameter.Direction) {
				case Direction.Input:
					return "global::System.Data.ParameterDirection.Input";
				case Direction.Output:
					return "global::System.Data.ParameterDirection.Output";
				case Direction.InputOutput:
					return "global::System.Data.ParameterDirection.InputOutpu";
				default:
					System.Diagnostics.Debug.Assert(false, "", "Unknown parameter direction: \"{0}\".", parameter.Direction);
					return "global::System.Data.ParameterDirection.Input";
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="parameter"></param>
		/// <returns></returns>
		private string GetParameterActualValue(DbParameter parameter) {
			System.Diagnostics.Debug.Assert(parameter != null);

			if (parameter.Direction == Direction.Output) {
				return "global::System.DBNull.Value";
			} else {
				return EscapeKeyword(ToCamel(parameter.Name));
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="text"></param>
		/// <returns></returns>
		private string GetDynamicTextType(DbDynamicTextItem text) {
			System.Diagnostics.Debug.Assert(text != null);

			switch (text.DynamicTextType) {
				case DynamicTextType.Exists:
					return "MyLib.DbDynamicTextType.Exists";
				case DynamicTextType.NotExists:
					return "MyLib.DbDynamicTextType.NotExists";
				case DynamicTextType.IsEmpty:
					return "MyLib.DbDynamicTextType.IsEmpty";
				case DynamicTextType.IsNotEmpty:
					return "MyLib.DbDynamicTextType.IsNotEmpty";
				case DynamicTextType.IsNull:
					return "MyLib.DbDynamicTextType.IsNull";
				case DynamicTextType.IsNotNull:
					return "MyLib.DbDynamicTextType.IsNotNull";
				case DynamicTextType.IsNullOrEmpty:
					return "MyLib.DbDynamicTextType.IsNullOrEmpty";
				default:
					System.Diagnostics.Debug.Assert(false, "", "Unknown dynamic text type: \"{0}\".", text.DynamicTextType);
					return "'MyLib.DbDynamicTextType.Exists";
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="column"></param>
		/// <returns></returns>
		private string GetColumnType(DbColumn column) {
			switch (column.DbType) {
				case DbType.String:
					return "string";
				case DbType.Integer:
					return "int";
				case DbType.Long:
					return "long";
				case DbType.Decimal:
					return "decimal";
				case DbType.Double:
					return "double";
				case DbType.Date:
				case DbType.DateTime:
				case DbType.StartDate:
				case DbType.StartMonth:
				case DbType.StartYear:
				case DbType.EndDate:
				case DbType.EndMonth:
				case DbType.EndYear:
					return "global::System.DateTime";
				case DbType.Boolean:
					return "bool";
				case DbType.Binary:
					return "byte[]";
				default:
					System.Diagnostics.Debug.Assert(false, "", "Unknown db type: \"{0}\".", column.DbType);
					return "void";
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="column"></param>
		/// <returns></returns>
		private string GetColumnDefaultValue(DbColumn column) {
			var defaultValue = column.DefaultValue;

			System.Diagnostics.Debug.Assert(defaultValue != null);

			switch (column.DbType) {
				case DbType.String:
					return "\"" + defaultValue.Replace("\\", "\\\\").Replace("\"", "\\\"") + "\"";
				case DbType.Integer:
					return defaultValue;
				case DbType.Long:
					return defaultValue + "L";
				case DbType.Decimal:
					return defaultValue + "M";
				case DbType.Double:
					return defaultValue + "D";
				case DbType.Date:
				case DbType.DateTime:
				case DbType.EndDate:
				case DbType.EndMonth:
				case DbType.EndYear:
					DateTime ddt;
					if (!DateTime.TryParse(defaultValue, out ddt)) {
						throw new InvalidDbObjectException(column, Properties.Main.ErrorInvalidLiteralValue,
							defaultValue, typeof(DateTime));
					} else {
						ddt = ddt.ToUniversalTime();
						return string.Format("new global::System.DateTime({0}, {1}, {2}, {3}, {4}, {5}, {6}, global::System.DateTimeKind.Utc)",
							ddt.Year, ddt.Month, ddt.Day, ddt.Hour, ddt.Minute, ddt.Second, ddt.Millisecond);
					}
				case DbType.Boolean:
					var bv = defaultValue.ToLower();
					if (bv == "true" || bv == "1" || bv == "yes") {
						return "true";
					} else if (bv == "false" || bv == "0" || bv == "no") {
						return "false";
					} else {
						throw new InvalidDbObjectException(column, Properties.Main.ErrorInvalidLiteralValue,
							defaultValue, typeof(bool));
					}
				case DbType.Binary:
					var av = defaultValue.Split(',');
					var bav = new StringBuilder();
					for (int i = 0; i < av.Length; ++i) {
						byte avb;
						System.Globalization.NumberStyles ns;
						av[i] = av[i].Trim().ToLower();
						if (av[i].StartsWith("0x")) {
							ns = System.Globalization.NumberStyles.HexNumber;
							av[i] = av[i].Substring(2);
						} else {
							ns = System.Globalization.NumberStyles.Integer;
						}
						if (byte.TryParse(av[i], ns, null, out avb)) {
							bav.AppendFormat("0x{0:x2}", avb);
							if (i < av.Length - 1) {
								bav.Append(", ");
							}
						} else {
							throw new InvalidDbObjectException(column, Properties.Main.ErrorInvalidLiteralValue,
								defaultValue, typeof(byte[]));
						}
					}
					return "new byte[] { " + bav.ToString() + " }";
				default:
					System.Diagnostics.Debug.Assert(false, "", "Unknown db type: \"{0}\".", column.DbType);
					return "null";
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="statement"></param>
		/// <returns></returns>
		private string GetStatementResultType(DbStatement statement) {
			switch (statement.ResultType) {
				case ResultType.String:
					return "string";
				case ResultType.Integer:
					return "int";
				case ResultType.Short:
					return "short";
				case ResultType.Long:
					return "long";
				case ResultType.Decimal:
					return "decimal";
				case ResultType.Double:
					return "double";
				case ResultType.DateTime:
					return "global::System.DateTime";
				case ResultType.Boolean:
					return "bool";
				case ResultType.Binary:
					return "byte[]";
				case ResultType.StringList:
					return "global::System.Collections.Generic.IList<string>";
				case ResultType.IntegerList:
					return "global::System.Collections.Generic.IList<int>";
				case ResultType.ShortList:
					return "global::System.Collections.Generic.IList<short>";
				case ResultType.LongList:
					return "global::System.Collections.Generic.IList<long>";
				case ResultType.DecimalList:
					return "global::System.Collections.Generic.IList<decimal>";
				case ResultType.DoubleList:
					return "global::System.Collections.Generic.IList<double>";
				case ResultType.DateTimeList:
					return "global::System.Collections.Generic.IList<global::System.DateTime>";
				case ResultType.BooleanList:
					return "global::System.Collections.Generic.IList<bool>";
				case ResultType.BinaryList:
					return "global::System.Collections.Generic.IList<byte[]>";
				case ResultType.Table:
				case ResultType.TableSingleRow:
					if (string.IsNullOrWhiteSpace(statement.ResultTable)) {
						return "global::System.Data.DataTable";
					} else {
						return ToPascal(statement.ResultTable) + "Table";
					}
				case ResultType.UpdatedRows:
					return "int";
				default:
					System.Diagnostics.Debug.Assert(false, "", "Unknown result type: \"{0}\".", statement.ResultType);
					return "void";
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="statement"></param>
		/// <returns></returns>
		private string GetStatementCommandType(DbStatement statement) {
			System.Diagnostics.Debug.Assert(statement != null);

			switch (statement.CommandType) {
				case CommandType.Text:
					return "global::System.Data.CommandType.Text";
				case CommandType.StoredProcedure:
					return "global::System.Data.CommandType.StoredProcedure";
				default:
					System.Diagnostics.Debug.Assert(false, "", "Unknown command type: \"{0}\".", statement.CommandType);
					return "global::System.Data.CommandType.Text";
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="s"></param>
		/// <returns></returns>
		private string EncodeSql(string s) {
			if (string.IsNullOrEmpty(s)) {
				return string.Empty;
			}

			var ret = new StringBuilder();
			foreach (var c in s) {
				if (c == '"') {
					ret.Append("\"\"");
				} else if (c == '\r' || c == '\n') {
					continue;
				} else {
					ret.Append(c);
				}
			}

			return ret.ToString();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="writer"></param>
		/// <param name="comment"></param>
		/// <param name="args"></param>
		private void WriteComment(CodeTextWriter writer, string comment, params object[] args) {
			writer.WriteLine("/// <summary>");
			writer.Write("/// ");
			writer.WriteLine(comment, args);
			writer.WriteLine("/// </summary>");
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="writer"></param>
		/// <param name="obj"></param>
		/// <param name="defaultValue"></param>
		/// <param name="args"></param>
		private void WriteComment(CodeTextWriter writer, DbObject obj, string defaultValue, params object[] args) {
			writer.WriteLine("/// <summary>");
			writer.Write("/// ");
			if (string.IsNullOrEmpty(obj.Description)) {
				writer.WriteLine(defaultValue, args);
			} else {
				writer.WriteLine(obj.Description);
			}
			writer.WriteLine("/// <para>__LINE__: {0}</para>", obj.LineInfo);
			writer.WriteLine("/// </summary>");
		}

		#endregion

		/// <summary>
		/// 
		/// </summary>
		/// <param name="writer"></param>
		private void WriteHead(CodeTextWriter writer) {
			var version = GetType().Assembly.GetName().Version;

			writer.WriteLine(Properties.Main.CodeFileHeadComment, version, DateTime.Now);
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="writer"></param>
		/// <param name="namespace"></param>
		private void WriteStartNamespace(CodeTextWriter writer, string @namespace) {
			writer.WriteLine("namespace {0}", @namespace);
			writer.WriteLine("{");
			writer.Indent();

			writer.WriteLine("using global::System.Collections.Generic;");
			writer.WriteLine("using global::System.Text;");
			writer.WriteLine();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="writer"></param>
		private void WriteEndNamespace(CodeTextWriter writer, string @namespace) {
			writer.Unindent();
			writer.WriteLine("}} // end of {0}.", @namespace);
		}

		#region Method for DbTable

		/// <summary>
		/// 
		/// </summary>
		/// <param name="writer"></param>
		private void WriteTable(CodeTextWriter writer, DbTable table) {
			var tableName = ToPascal(table.Name) + "Table";
			var tableRowName = ToPascal(table.Name) + "Row";

			WriteComment(writer, table, tableName);
			writer.WriteLine(@"[System.ComponentModel.DesignerCategory(""Code"")]");
			writer.WriteLine("public class {0} : global::System.Data.DataTable, global::System.Collections.Generic.IList<{1}>", tableName, tableRowName);
			writer.WriteLine("{");
			writer.Indent();

			WriteTableColumnIndexes(writer, table);

			WriteTableCreateInstance(writer, table);

			WriteTableNewRowFromBuilder(writer, table);

			WriteTableCount(writer, table);

			WriteTableIndex(writer, table);

			WriteTableAddRow(writer, table);

			WriteTableRemoveRow(writer, table);

			WriteTableAddNewRow(writer, table);

			WriteTableGetRowType(writer, table);

			writer.WriteLine();
			writer.WriteLine("#region Members of System.Collections.Generic.IList<{0}>", tableRowName);
			writer.WriteLine();

			WriteTableIndexOf(writer, table);
			WriteTableInsert(writer, table);
			WriteTableRemoveAt(writer, table);
			WriteTableAdd(writer, table);
			WriteTableContains(writer, table);
			WriteTableContainsKey(writer, table);
			WriteTableCopyTo(writer, table);
			WriteTableIsReadOnly(writer, table);
			WriteTableRemove(writer, table);
			WriteTableGetEnumerator(writer, table);
			WriteTableGetEnumerator2(writer, table);

			writer.WriteLine("#endregion");
			writer.WriteLine();

			WriteTableTable(writer, table);

			writer.Unindent();
			writer.WriteLine("}} // end of {0}", tableName);
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="writer"></param>
		/// <param name="columns"></param>
		private void WriteTableColumnIndexes(CodeTextWriter writer, IList<DbColumn> columns) {
			for (int i = 0; i < columns.Count; ++i) {
				var columnName = ToPascal(columns[i].Name);

				WriteComment(writer, columns[i], "Index of column \"{0}\"", columnName);
				writer.WriteLine("public const int Column{0} = {1};", columnName, i);
				writer.WriteLine();
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="writer"></param>
		/// <param name="table"></param>
		private void WriteTableCreateInstance(CodeTextWriter writer, DbTable table) {
			var tableName = ToPascal(table.Name) + "Table";
			WriteComment(writer, @"Creates a new instance of {0}.", tableName);
			writer.WriteLine("/// <returns>A new instance of {0}.</returns>", tableName);
			writer.WriteLine("protected override global::System.Data.DataTable CreateInstance()");
			writer.WriteLine("{");
			writer.Indent();
			writer.WriteLine("return new {0}();", tableName);
			writer.Unindent();
			writer.WriteLine("}");
			writer.WriteLine();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="writer"></param>
		/// <param name="table"></param>
		private void WriteTableNewRowFromBuilder(CodeTextWriter writer, DbTable table) {
			var rowName = ToPascal(table.Name) + "Row";
			WriteComment(writer, @"Creates a new instance of {0}.", rowName);
			writer.WriteLine("/// <param name=\"builder\">Builder for data row.</param>");
			writer.WriteLine("/// <returns>A new instance of {0}.</returns>", rowName);
			writer.WriteLine("protected override global::System.Data.DataRow NewRowFromBuilder(global::System.Data.DataRowBuilder builder)");
			writer.WriteLine("{");
			writer.Indent();
			writer.WriteLine("return new {0}(builder);", rowName);
			writer.Unindent();
			writer.WriteLine("}");
			writer.WriteLine();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="writer"></param>
		/// <param name="table"></param>
		private void WriteTableCount(CodeTextWriter writer, DbTable table) {
			WriteComment(writer, "Gets count of rows.");
			writer.WriteLine("public int Count");
			writer.WriteLine("{");
			writer.Indent();
			writer.WriteLine("get");
			writer.WriteLine("{");
			writer.Indent();
			writer.WriteLine("return base.Rows.Count;");
			writer.Unindent();
			writer.WriteLine("}");
			writer.Unindent();
			writer.WriteLine("}");
			writer.WriteLine();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="writer"></param>
		/// <param name="table"></param>
		private void WriteTableIndex(CodeTextWriter writer, DbTable table) {
			var rowName = ToPascal(table.Name) + "Row";
			WriteComment(writer, "Gets a row.");
			writer.WriteLine("/// <param name=\"index\">Index of row.</param>");
			writer.WriteLine("public {0} this[int index]", rowName);
			writer.WriteLine("{");
			writer.Indent();
			writer.WriteLine("get");
			writer.WriteLine("{");
			writer.Indent();
			writer.WriteLine("return ({0})(base.Rows[index]);", rowName);
			writer.Unindent();
			writer.WriteLine("}");
			writer.WriteLine("set");
			writer.WriteLine("{");
			writer.Indent();
			writer.WriteLine("throw new global::System.NotImplementedException();");
			writer.Unindent();
			writer.WriteLine("}");
			writer.Unindent();
			writer.WriteLine("}");
			writer.WriteLine();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="writer"></param>
		/// <param name="table"></param>
		private void WriteTableAddRow(CodeTextWriter writer, DbTable table) {
			var rowName = ToPascal(table.Name) + "Row";
			WriteComment(writer, @"Adds a row.");
			writer.WriteLine("/// <param name=\"row\">Row which will be added.</param>");
			writer.WriteLine("public void AddRow({0} row)", rowName);
			writer.WriteLine("{");
			writer.Indent();
			writer.WriteLine("base.Rows.Add(row);");
			writer.Unindent();
			writer.WriteLine("}");
			writer.WriteLine();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="writer"></param>
		/// <param name="table"></param>
		private void WriteTableRemoveRow(CodeTextWriter writer, DbTable table) {
			var rowName = ToPascal(table.Name) + "Row";
			WriteComment(writer, @"Removes a row.");
			writer.WriteLine("/// <param name=\"row\">Row which will be removed.</param>");
			writer.WriteLine("public void RemoveRow({0} row)", rowName);
			writer.WriteLine("{");
			writer.Indent();
			writer.WriteLine("base.Rows.Remove(row);");
			writer.Unindent();
			writer.WriteLine("}");
			writer.WriteLine();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="writer"></param>
		/// <param name="table"></param>
		private void WriteTableAddNewRow(CodeTextWriter writer, DbTable table) {
			var rowName = ToPascal(table.Name) + "Row";
			WriteComment(writer, @"Adds a new row with default values.");
			writer.WriteLine("/// <returns>A new added row with default values.</returns>");
			writer.WriteLine("public {0} AddNewRow()", rowName);
			writer.WriteLine("{");
			writer.Indent();
			writer.WriteLine("var row = ({0})base.NewRow();", rowName);
			writer.WriteLine();
			writer.WriteLine("global::System.Diagnostics.Debug.Assert(row != null);");
			writer.WriteLine();
			writer.WriteLine("base.Rows.Add(row);");
			writer.WriteLine();
			writer.WriteLine("return row;");
			writer.Unindent();
			writer.WriteLine("}");
			writer.WriteLine();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="writer"></param>
		/// <param name="table"></param>
		private void WriteTableGetRowType(CodeTextWriter writer, DbTable table) {
			var rowName = ToPascal(table.Name) + "Row";
			WriteComment(writer, @"Gets type of data row.");
			writer.WriteLine("/// <returns>Type of data row.<returns>");
			writer.WriteLine("protected override global::System.Type GetRowType()");
			writer.WriteLine("{");
			writer.Indent();
			writer.WriteLine("return typeof({0});", rowName);
			writer.Unindent();
			writer.WriteLine("}");
			writer.WriteLine();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="writer"></param>
		/// <param name="table"></param>
		private void WriteTableIndexOf(CodeTextWriter writer, DbTable table) {
			var rowName = ToPascal(table.Name) + "Row";
			WriteComment(writer, @"Gets index of this row.");
			writer.WriteLine("/// <param name=\"item\">Row</param>");
			writer.WriteLine("/// <returns></returns>");
			writer.WriteLine("public int IndexOf({0} item)", rowName);
			writer.WriteLine("{");
			writer.Indent();
			writer.WriteLine("return base.Rows.IndexOf(item);");
			writer.Unindent();
			writer.WriteLine("}");
			writer.WriteLine();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="writer"></param>
		/// <param name="table"></param>
		private void WriteTableInsert(CodeTextWriter writer, DbTable table) {
			var rowName = ToPascal(table.Name) + "Row";
			WriteComment(writer, @"Insert a row.");
			writer.WriteLine("/// <param name=\"item\">Row which will be inserted.</param>");
			writer.WriteLine("/// <param name=\"index\">Index of inserted row.</param>");
			writer.WriteLine("public void Insert(int index, {0} item)", rowName);
			writer.WriteLine("{");
			writer.Indent();
			writer.WriteLine("base.Rows.InsertAt(item, index);");
			writer.Unindent();
			writer.WriteLine("}");
			writer.WriteLine();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="writer"></param>
		/// <param name="table"></param>
		private void WriteTableRemoveAt(CodeTextWriter writer, DbTable table) {
			var rowName = ToPascal(table.Name) + "Row";
			WriteComment(writer, "Remove a row.");
			writer.WriteLine("/// <param name=\"index\">Index of removed row.</param>");
			writer.WriteLine("public void RemoveAt(int index)");
			writer.WriteLine("{");
			writer.Indent();
			writer.WriteLine("base.Rows.RemoveAt(index);");
			writer.Unindent();
			writer.WriteLine("}");
			writer.WriteLine();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="writer"></param>
		/// <param name="table"></param>
		private void WriteTableAdd(CodeTextWriter writer, DbTable table) {
			var rowName = ToPascal(table.Name) + "Row";
			WriteComment(writer, @"Add a row.");
			writer.WriteLine("/// <param name=\"item\">Row which will be inserted.</param>");
			writer.WriteLine("public void Add({0} item)", rowName);
			writer.WriteLine("{");
			writer.Indent();
			writer.WriteLine("base.Rows.Add(item);");
			writer.Unindent();
			writer.WriteLine("}");
			writer.WriteLine();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="writer"></param>
		/// <param name="table"></param>
		private void WriteTableContains(CodeTextWriter writer, DbTable table) {
			var rowName = ToPascal(table.Name) + "Row";
			WriteComment(writer, @"Contains a row.");
			writer.WriteLine("/// <param name=\"item\">Row.</param>");
			writer.WriteLine("/// <returns></returns>");
			writer.WriteLine("bool global::System.Collections.Generic.ICollection<{0}>.Contains({0} item)", rowName);
			writer.WriteLine("{");
			writer.Indent();
			writer.WriteLine("throw new System.NotImplementedException();");
			writer.Unindent();
			writer.WriteLine("}");
			writer.WriteLine();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="writer"></param>
		/// <param name="table"></param>
		private void WriteTableContainsKey(CodeTextWriter writer, DbTable table) {
			WriteComment(writer, @"Contains key");
			writer.WriteLine("/// <param name=\"keys\">Keys of row.</param>");
			writer.WriteLine("/// <returns></returns>");
			writer.WriteLine("public bool ContainsKey(object[] keys)");
			writer.WriteLine("{");
			writer.Indent();
			writer.WriteLine("global::System.Diagnostics.Debug.Assert(keys != null);");
			writer.WriteLine("global::System.Diagnostics.Debug.Assert(keys.Length != 0);");
			writer.WriteLine();
			writer.WriteLine("return base.Rows.Contains(keys);");
			writer.Unindent();
			writer.WriteLine("}");
			writer.WriteLine();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="writer"></param>
		/// <param name="table"></param>
		private void WriteTableCopyTo(CodeTextWriter writer, DbTable table) {
			var rowName = ToPascal(table.Name) + "Row";
			WriteComment(writer, @"Copy rows to an array.");
			writer.WriteLine("/// <param name=\"array\">Array that will be copied to.</param>");
			writer.WriteLine("/// <param name=\"arrayIndex\">Index of first element.</param");
			writer.WriteLine("public void CopyTo({0}[] array, int arrayIndex)", rowName);
			writer.WriteLine("{");
			writer.Indent();
			writer.WriteLine("base.Rows.CopyTo(array, arrayIndex);");
			writer.Unindent();
			writer.WriteLine("}");
			writer.WriteLine();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="writer"></param>
		/// <param name="table"></param>
		private void WriteTableIsReadOnly(CodeTextWriter writer, DbTable table) {
			WriteComment(writer, "");
			writer.WriteLine("public bool IsReadOnly");
			writer.WriteLine("{");
			writer.Indent();
			writer.WriteLine("get");
			writer.WriteLine("{");
			writer.Indent();
			writer.WriteLine("return false;");
			writer.Unindent();
			writer.WriteLine("}");
			writer.Unindent();
			writer.WriteLine("}");
			writer.WriteLine();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="writer"></param>
		/// <param name="table"></param>
		private void WriteTableRemove(CodeTextWriter writer, DbTable table) {
			var rowName = ToPascal(table.Name) + "Row";
			WriteComment(writer, "Removes a row.");
			writer.WriteLine("/// <param name=\"item\">Row which will be removed.</param>");
			writer.WriteLine("public bool Remove({0} item)", rowName);
			writer.WriteLine("{");
			writer.Indent();
			writer.WriteLine("base.Rows.Remove(item);");
			writer.WriteLine();
			writer.WriteLine("return true;");
			writer.Unindent();
			writer.WriteLine("}");
			writer.WriteLine();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="writer"></param>
		/// <param name="table"></param>
		private void WriteTableGetEnumerator(CodeTextWriter writer, DbTable table) {
			var rowName = ToPascal(table.Name) + "Row";
			WriteComment(writer, "");
			writer.WriteLine("/// <returns></returns>");
			writer.WriteLine("public global::System.Collections.Generic.IEnumerator<{0}> GetEnumerator()", rowName);
			writer.WriteLine("{");
			writer.Indent();
			writer.WriteLine("var iter = base.Rows.GetEnumerator();");
			writer.WriteLine("while (iter.MoveNext())");
			writer.WriteLine("{");
			writer.Indent();
			writer.WriteLine("yield return ({0})iter.Current;", rowName);
			writer.Unindent();
			writer.WriteLine("}");
			writer.Unindent();
			writer.WriteLine("}");
			writer.WriteLine();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="writer"></param>
		/// <param name="table"></param>
		private void WriteTableGetEnumerator2(CodeTextWriter writer, DbTable table) {
			WriteComment(writer, "");
			writer.WriteLine("/// <returns></returns>");
			writer.WriteLine("global::System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()");
			writer.WriteLine("{");
			writer.Indent();
			writer.WriteLine(" return base.Rows.GetEnumerator();");
			writer.Unindent();
			writer.WriteLine("}");
			writer.WriteLine();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="writer"></param>
		/// <param name="table"></param>
		private void WriteTableTable(CodeTextWriter writer, DbTable table) {
			var tableName = ToPascal(table.Name) + "Table";
			WriteComment(writer, "Initialize data table.");
			writer.WriteLine("public {0}()", tableName);
			writer.WriteLine("{");
			writer.Indent();
			writer.WriteLine("base.TableName = \"{0}\";", tableName);
			writer.WriteLine("base.BeginInit();");
			writer.WriteLine();

			var pkeys = new List<int>();
			for (int i = 0; i < table.Count; ++i) {
				var column = table[i];

				writer.WriteLine("var column{0} = new global::System.Data.DataColumn(\"{1}\", typeof({2}), null, global::System.Data.MappingType.Attribute);",
					i, ToCamel(column.Name), GetColumnType(column));

				if (column.Size > 0) {
					writer.WriteLine("column{0}.MaxLength = {1};", i, column.Size);
				}

				if (column.Unique ?? false) {
					writer.WriteLine("column{0}.Unique = true;", i);
				}

				if (column.PrimaryKey ?? false) {
					pkeys.Add(i);
				}

				if (column.DefaultValue != null) {
					writer.WriteLine("column{0}.DefaultValue = {1};", i, GetColumnDefaultValue(column));
				}

				writer.WriteLine("base.Columns.Add(column{0});", i);
			}

			if (pkeys.Count != 0) {
				writer.WriteLine();
				writer.WriteLine("base.PrimaryKey = new global::System.Data.DataColumn[] {{ {0} }};",
					string.Join(", ", pkeys.Select(pk => "column" + pk))
					);
			}

			writer.WriteLine();
			writer.WriteLine("base.EndInit();");
			writer.Unindent();
			writer.WriteLine("}");
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="writer"></param>
		/// <param name="table"></param>
		private void WriteTableRow(CodeTextWriter writer, DbTable table) {
			var tableRowName = ToPascal(table.Name) + "Row";

			WriteComment(writer, table, tableRowName);
			writer.WriteLine(@"[System.ComponentModel.DesignerCategory(""Code"")]");
			writer.WriteLine("public class {0} : global::System.Data.DataRow", tableRowName);
			writer.WriteLine("{");
			writer.Indent();

			WriteTableRowTableRow(writer, table);

			WriteTableRowColumn(writer, table);

			writer.Unindent();
			writer.WriteLine("}} // end of {0}.", tableRowName);
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="writer"></param>
		/// <param name="table"></param>
		private void WriteTableRowTableRow(CodeTextWriter writer, DbTable table) {
			var rowName = ToPascal(table.Name) + "Row";
			WriteComment(writer, "Initialize data row.");
			writer.WriteLine("public {0} (global::System.Data.DataRowBuilder builder) : base(builder)", rowName);
			writer.WriteLine("{");
			writer.Indent();
			// Empty!
			writer.Unindent();
			writer.WriteLine("}");
			writer.WriteLine();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="writer"></param>
		/// <param name="table"></param>
		private void WriteTableRowColumn(CodeTextWriter writer, DbTable table) {
			var tableName = ToPascal(table.Name) + "Table";
			foreach (var column in table) {
				WriteTableRowColumn(writer, tableName, column);
				WriteTableRowSetColumnNull(writer, tableName, column);
				WriteTableRowIsColumnNull(writer, tableName, column);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="writer"></param>
		/// <param name="tableName"></param>
		/// <param name="column"></param>
		private void WriteTableRowColumn(CodeTextWriter writer, string tableName, DbColumn column) {
			var columnName = ToPascal(column.Name);
			WriteComment(writer, column, "Gets or sets \"{0}\".", columnName);
			writer.WriteLine("public {0} {1}", GetColumnType(column), columnName);
			writer.WriteLine("{");
			writer.Indent();

			writer.WriteLine("get");
			writer.WriteLine("{");
			writer.Indent();
			writer.WriteLine("var col = base.Table.Columns[{0}.Column{1}];", tableName, columnName);
			writer.WriteLine();
			writer.WriteLine("if (base.IsNull(col))");
			writer.WriteLine("{");
			writer.Indent();
			writer.WriteLine("throw new global::System.NullReferenceException(\"Value of Column{0} is null!\");", columnName);
			writer.Unindent();
			writer.WriteLine("}");
			writer.WriteLine("else");
			writer.WriteLine("{");
			writer.Indent();
			writer.WriteLine("return ({0})(base[col]);", GetColumnType(column));
			writer.Unindent();
			writer.WriteLine("}");
			writer.Unindent();
			writer.WriteLine("}");

			writer.WriteLine("set");
			writer.WriteLine("{");
			writer.Indent();
			writer.WriteLine("var col = base.Table.Columns[{0}.Column{1}];", tableName, columnName);
			writer.WriteLine("base[col] = value;");
			writer.Unindent();
			writer.WriteLine("}");

			writer.Unindent();
			writer.WriteLine("}");
			writer.WriteLine();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="writer"></param>
		/// <param name="tableName"></param>
		/// <param name="column"></param>
		private void WriteTableRowSetColumnNull(CodeTextWriter writer, string tableName, DbColumn column) {
			var columnName = ToPascal(column.Name);
			WriteComment(writer, "Sets Column\"{0}\" to null.", columnName);
			writer.WriteLine("public void Set{0}Null()", columnName);
			writer.WriteLine("{");
			writer.Indent();
			writer.WriteLine("var col = base.Table.Columns[{0}.Column{1}];", tableName, columnName);
			writer.WriteLine("base[col] = global::System.Convert.DBNull;");
			writer.Unindent();
			writer.WriteLine("}");
			writer.WriteLine();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="writer"></param>
		/// <param name="tableName"></param>
		/// <param name="column"></param>
		private void WriteTableRowIsColumnNull(CodeTextWriter writer, string tableName, DbColumn column) {
			var columnName = ToPascal(column.Name);
			WriteComment(writer, "Get whether Column\"{0}\" is null.", columnName);
			writer.WriteLine("public bool Is{0}Null", columnName);
			writer.WriteLine("{");
			writer.Indent();
			writer.WriteLine("get");
			writer.WriteLine("{");
			writer.Indent();
			writer.WriteLine("var col = base.Table.Columns[{0}.Column{1}];", tableName, columnName);
			writer.WriteLine("return base.IsNull(col);");
			writer.Unindent();
			writer.WriteLine("}");
			writer.Unindent();
			writer.WriteLine("}");
			writer.WriteLine();
		}

		#endregion

		#region Method for DbModule

		/// <summary>
		/// 
		/// </summary>
		/// <param name="writer"></param>
		/// <param name="module"></param>
		private void WriteModule(CodeTextWriter writer, DbModule module) {
			var moduleName = ToPascal(module.Name) + "Provider";
			WriteComment(writer, module, "{0}", moduleName);

			writer.WriteLine("public class {0} :  MyLib.DbContextProvider", moduleName);
			writer.WriteLine("{");
			writer.Indent();

			// Write statement(s).
			foreach (var statement in module.Statements) {
				WriteStatement(writer, statement);
			}

			writer.Unindent();
			writer.WriteLine("}} // end of {0}.", moduleName);
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="writer"></param>
		/// <param name="statement"></param>
		private void WriteStatement(CodeTextWriter writer, DbStatement statement) {
			var statementName = ToPascal(statement.Name);
			var allowPaging = statement.AllowPaging ?? (statement.ResultType == ResultType.Table);
			var readOnly = statement.ReadOnly ?? (statement.ResultType != ResultType.UpdatedRows);

			WriteComment(writer, statement, "{0}", statementName);

			WriteStatementHead(writer, statement, statementName, allowPaging);

			writer.WriteLine("{");
			writer.Indent();

			WriteParameterAssignment(writer, statement);

			WriteStatementCreateSql(writer, statement);

			if (readOnly) {
				writer.WriteLine("using (var session = SessionFactory.CreateReadOnlySession())");
			} else {
				writer.WriteLine("using (var session = SessionFactory.CreateSession())");
			}
			writer.WriteLine("{");
			writer.Indent();

			WriteStatementDoQuery(writer, statement, allowPaging);

			if (!readOnly) {
				writer.WriteLine("session.Commit();");
				writer.WriteLine();
			}

			WriteOutputParameterAssignment(writer, statement);

			writer.WriteLine("return c;");
			writer.Unindent();
			writer.WriteLine("} // end of using.");

			writer.Unindent();
			writer.WriteLine("}} // end of {0}.", statementName);
			writer.WriteLine();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="writer"></param>
		/// <param name="statement"></param>
		/// <param name="statementName"></param>
		/// <param name="allowPaging"></param>
		private void WriteStatementHead(CodeTextWriter writer, DbStatement statement, string statementName, bool allowPaging) {
			writer.WriteLine("public virtual {0} {1} (",
				GetStatementResultType(statement),
				statementName);
			writer.Indent();
			var iter = statement.Parameters.GetEnumerator();
			var hasNext = iter.MoveNext();
			while (hasNext) {
				WriteParameter(writer, iter.Current);
				hasNext = iter.MoveNext();
				if (hasNext) {
					writer.WriteLine(",");
				} else {
					if (allowPaging) {
						writer.WriteLine(",");
					}
					break;
				}
			}
			if (allowPaging) {
				writer.WriteLine("int? maximumRows = null,");
				writer.Write("int? startRowIndex = null");
			}
			writer.WriteLine(" )");
			writer.Unindent();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="writer"></param>
		/// <param name="parameter"></param>
		private void WriteParameter(CodeTextWriter writer, DbParameter parameter) {
			var parameterName = EscapeKeyword(ToCamel(parameter.Name));
			if (parameter.Direction == Direction.Input) {
				writer.Write("{0} {1} = {2}", GetParameterType(parameter), parameterName, GetParameterDefaultValue(parameter));
			} else if (parameter.Direction == Direction.Output) {
				writer.Write("out {0} {1}", GetParameterType(parameter), parameterName);
			} else if (parameter.Direction == Direction.InputOutput) {
				writer.Write("ref {0} {1} = {2}", GetParameterType(parameter), parameterName, GetParameterDefaultValue(parameter));
			} else {
				System.Diagnostics.Debug.Assert(false, "", "Unknown direction: \"{0}\".", parameter.Direction);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="writer"></param>
		/// <param name="statement"></param>
		private void WriteParameterAssignment(CodeTextWriter writer, DbStatement statement) {
			writer.WriteLine("var parameters = new MyLib.DbParameter[{0}];", statement.Parameters.Count);
			for (int i = 0; i < statement.Parameters.Count; ++i) {
				var parameter = statement.Parameters[i];
				writer.WriteLine("parameters[{0}] = new MyLib.DbParameter(\"{1}\", {2}, {3}, {4}, {5}, {6});", i,
					parameter.Name, GetParameterDbType(parameter), GetParameterMatchType(parameter),
					parameter.Size, GetParameterDirection(parameter), GetParameterActualValue(parameter));
			}
			writer.WriteLine();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="writer"></param>
		/// <param name="statement"></param>
		private void WriteStatementCreateSql(CodeTextWriter writer, DbStatement statement) {
			writer.WriteLine("// Create sql.");
			writer.WriteLine("var sqlBuilder = new global::System.Text.StringBuilder();");
			writer.WriteLine();
			var dc = 0;
			foreach (var text in statement.Texts) {
				if (text is DbDynamicText) {
					WriteStatementDynamicSql(writer, (DbDynamicText)text, dc++);
				} else if (text is DbStaticText) {
					var lines = ((DbStaticText)text).Text.Split('\n');
					foreach (var line in lines) {
						writer.WriteLine("sqlBuilder.AppendLine(@\"{0}\");", EncodeSql(line));
					}
					writer.WriteLine();
				} else {
					System.Diagnostics.Debug.Assert(false, "", "Unknown db text: \"{0}\"", text.GetType());
				}
			}

			writer.WriteLine("// Sql text.");
			writer.WriteLine("var sql = sqlBuilder.ToString();");
			writer.WriteLine();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="writer"></param>
		/// <param name="text"></param>
		/// <param name="index"></param>
		private void WriteStatementDynamicSql(CodeTextWriter writer, DbDynamicText text, int index) {
			writer.WriteLine("// Create dynamic sql {0}.", index);
			writer.WriteLine("// __LINE__ : {0}", text.LineInfo);

			writer.WriteLine("var dynamicFlag{0} = false;", index);
			writer.WriteLine("var dynamicText{0} = new global::System.Text.StringBuilder();", index);
			writer.WriteLine();

			foreach (var item in text.Texts) {
				writer.WriteLine("if (IsDynamicMatched({0}, \"{1}\", parameters))", GetDynamicTextType(item), item.Parameter);
				writer.WriteLine("{");
				writer.Indent();
				writer.WriteLine("if (dynamicFlag{0})", index);
				writer.WriteLine("{");
				writer.Indent();
				writer.WriteLine("dynamicText{0}.AppendLine(\" and \");", index);
				writer.Unindent();
				writer.WriteLine("}");

				var lines = item.Text.Split('\n');
				foreach (var line in lines) {
					writer.WriteLine("dynamicText{0}.AppendLine(@\"{1}\");", index, EncodeSql(line));
				}
				writer.WriteLine("dynamicFlag{0} = true;", index);
				writer.Unindent();
				writer.WriteLine("}");
				writer.WriteLine();
			}

			writer.WriteLine("if (dynamicFlag{0})", index);
			writer.WriteLine("{");
			writer.Indent();
			if (!string.IsNullOrEmpty(text.Prepend)) {
				writer.WriteLine("sqlBuilder.Append(\" {0} \");", EncodeSql(text.Prepend));
			}
			writer.WriteLine("sqlBuilder.Append(dynamicText{0}.ToString());", index);
			writer.Unindent();
			writer.WriteLine("}");
			writer.WriteLine();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="writer"></param>
		/// <param name="statement"></param>
		/// <param name="allowPaging"></param>
		private void WriteStatementDoQuery(CodeTextWriter writer, DbStatement statement, bool allowPaging) {
			var commandType = GetStatementCommandType(statement);

			switch (statement.ResultType) {
				case ResultType.Table:
					if (allowPaging) {
						if (string.IsNullOrEmpty(statement.ResultTable)) {
							writer.WriteLine("var c = session.ExecutePagedQuery(sql, {0}, startRowIndex ?? 0, maximumRows ?? int.MaxValue, parameters);", commandType);
						} else {
							writer.WriteLine("var c = new {0}();", ToPascal(statement.ResultTable) + "Table");
							writer.WriteLine("session.ExecutePagedQuery(c, sql, {0}, startRowIndex ?? 0, maximumRows ?? int.MaxValue, parameters);", commandType);
						}
					} else {
						if (string.IsNullOrEmpty(statement.ResultTable)) {
							writer.WriteLine("var c = session.ExecuteQuery(sql, {0}, parameters);", commandType);
						} else {
							writer.WriteLine("var c = new {0}();", ToPascal(statement.ResultTable) + "Table");
							writer.WriteLine("session.ExecuteQuery(c, sql, {0}, parameters);", commandType);
						}
					}
					break;
				case ResultType.TableSingleRow:
					if (string.IsNullOrEmpty(statement.ResultTable)) {
						writer.WriteLine("var c = session.ExecuteSingleQuery(sql, {0}, parameters);", commandType);
					} else {
						writer.WriteLine("var c = new {0}();", ToPascal(statement.ResultTable) + "Table");
						writer.WriteLine("session.ExecuteSingleQuery(c, sql, {0}, parameters);", commandType);
					}
					break;
				case ResultType.String:
					writer.WriteLine("var c = session.ExecuteScalar<string>(sql, {0}, parameters);", commandType);
					break;
				case ResultType.Integer:
					writer.WriteLine("var c = session.ExecuteScalar<int>(sql, {0}, parameters);", commandType);
					break;
				case ResultType.Short:
					writer.WriteLine("var c = session.ExecuteScalar<short>(sql, {0}, parameters);", commandType);
					break;
				case ResultType.Long:
					writer.WriteLine("var c = session.ExecuteScalar<long>(sql, {0}, parameters);", commandType);
					break;
				case ResultType.Decimal:
					writer.WriteLine("var c = session.ExecuteScalar<decimal>(sql, {0}, parameters);", commandType);
					break;
				case ResultType.DateTime:
					writer.WriteLine("var c = session.ExecuteScalar<global::System.DateTime>(sql, {0}, parameters);", commandType);
					break;
				case ResultType.Double:
					writer.WriteLine("var c = session.ExecuteScalar<double>(sql, {0}, parameters);", commandType);
					break;
				case ResultType.Boolean:
					writer.WriteLine("var c = session.ExecuteScalar<bool>(sql, {0}, parameters);", commandType);
					break;
				case ResultType.StringList:
					writer.WriteLine("var c = session.ExecuteList<string>(sql, {0}, parameters);", commandType);
					break;
				case ResultType.IntegerList:
					writer.WriteLine("var c = session.ExecuteList<int>(sql, {0}, parameters);", commandType);
					break;
				case ResultType.ShortList:
					writer.WriteLine("var c = session.ExecuteList<short>(sql, {0}, parameters);", commandType);
					break;
				case ResultType.LongList:
					writer.WriteLine("var c = session.ExecuteList<long>(sql, {0}, parameters);", commandType);
					break;
				case ResultType.DecimalList:
					writer.WriteLine("var c = session.ExecuteList<decimal>(sql, {0}, parameters);", commandType);
					break;
				case ResultType.DateTimeList:
					writer.WriteLine("var c = session.ExecuteList<global::System.DateTime>(sql, {0}, parameters);", commandType);
					break;
				case ResultType.DoubleList:
					writer.WriteLine("var c = session.ExecuteList<double>(sql, {0}, parameters);", commandType);
					break;
				case ResultType.BooleanList:
					writer.WriteLine("var c = session.ExecuteList<bool>(sql, {0}, parameters);", commandType);
					break;
				case ResultType.UpdatedRows:
					writer.WriteLine("var c = session.ExecuteUpdate(sql, {0}, parameters);", commandType);
					break;
				default:
					System.Diagnostics.Debug.Assert(false, "", "Unknown result type: \"{0}\".", statement.ResultType);
					break;
			}
			writer.WriteLine();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="writer"></param>
		/// <param name="statement"></param>
		private void WriteOutputParameterAssignment(CodeTextWriter writer, DbStatement statement) {
			var hasContent = false;
			for (int i = 0; i < statement.Parameters.Count; ++i) {
				var parameter = statement.Parameters[i];
				if ((parameter.Direction & Direction.Output) == 0) {
					continue;
				}
				var parameterName = EscapeKeyword(ToCamel(parameter.Name));
				writer.WriteLine("{0} = ({1})global::System.Convert.ChangeType(parameters[{2}].Value, typeof({1}));",
					parameterName, GetParameterType(parameter), i);
				hasContent = true;
			}

			if (hasContent) {
				writer.WriteLine();
			}
		}

		#endregion

		/// <summary>
		/// 
		/// </summary>
		/// <param name="target"></param>
		/// <param name="logContext"></param>
		public CSharpCodeTemplate(string target, LogContext logContext) {
			m_target = target;
			m_logContext = logContext;
		}
	} // end of CSharpCodeTemplate.
}
